﻿#include "umenubutton.h"
#include "style/uproxystyle.h"

#include <QMenu>
#include <QStyleOptionButton>
#include <QStylePainter>
#include <QPainterPath>
#include <QWidgetAction>
#include <QStandardItem>
#include <QStandardItemModel>

class UMenuButtonPrivate
{
public:
    UMenuButtonPrivate(UMenuButton* q);
    ~UMenuButtonPrivate() {}

    void creatMenu(QStandardItem* item, QMenu* menu);

    QWidgetAction* creatWidgetAction(const QStandardItem* item, QMenu* sourceMenu);

    QAction* creatAction(const QStandardItem* item, QMenu* sourceMenu);

    void drawArrow(const QStyle::State &state, QPainter *p);

    void drawContent(QRect rect, const QStyle::State &state, QPainter *p);

    void drawBorder(const QStyle::State &state, QPainter *p);

private:
    Q_DECLARE_PUBLIC(UMenuButton)
    Q_DISABLE_COPY(UMenuButtonPrivate)
    UMenuButton* const q_ptr = nullptr;

    QScopedPointer<QMenu> menu;
    QStandardItemModel* model = nullptr;
    QMap<QAction*, QStandardItem*> actionMap;
    QSize arrowSize = QSize(16, 16);
    QSize iconSize = QSize(18, 18);
};

UMenuButtonPrivate::UMenuButtonPrivate(UMenuButton *q)
    : q_ptr(q), menu(new QMenu())
{
    model = new QStandardItemModel(q);
}

void UMenuButtonPrivate::creatMenu(QStandardItem* item, QMenu *menu)
{
    if ((nullptr == item) || (nullptr == menu))
    {
        return;
    }

    if (!item->hasChildren())
    {
        QAction* action = creatAction(item, menu);
        actionMap.insert(action, item);
    }
    else
    {
        QMenu* childMenu = menu->addMenu(item->icon(), item->text());
        if (!item->isEnabled())
        {
            childMenu->setEnabled(false);
            return;
        }
        for (int i = 0; i < item->rowCount(); ++i)
        {
            auto childItem = item->child(i);
            if (nullptr == childItem)
            {
                continue;
            }
            creatMenu(childItem, childMenu);
        }
    }
}

QWidgetAction* UMenuButtonPrivate::creatWidgetAction(const QStandardItem* item, QMenu* sourceMenu)
{
    QWidget* customAciton = new QWidget(sourceMenu);
    QWidgetAction* pAction = new QWidgetAction(sourceMenu);
    pAction->setDefaultWidget(customAciton);
    sourceMenu->addAction(pAction);

    return pAction;
}

QAction *UMenuButtonPrivate::creatAction(const QStandardItem *item, QMenu *sourceMenu)
{
    QAction* pAction = new QAction(sourceMenu);
    pAction->setText(item->text());
    pAction->setToolTip(item->toolTip());
    pAction->setIcon(item->icon());
    sourceMenu->addAction(pAction);

    return pAction;
}

void UMenuButtonPrivate::drawArrow(const QStyle::State &state, QPainter *p)
{
    Q_Q(UMenuButton);
    p->save();
    QRect rect = QRect(QPoint(q->width() - 20, (q->height() - arrowSize.height()) / 2), arrowSize);
    QIcon icon = q->isDown() ? QIcon::fromTheme("utk_arrowup") : QIcon::fromTheme("utk_arrowdown");
    p->drawPixmap(rect, UProxyStyle::getIconPixmap(state, icon, arrowSize));
    p->restore();
}

void UMenuButtonPrivate::drawContent(QRect rect, const QStyle::State &state, QPainter *p)
{
    Q_Q(UMenuButton);
    QRect iconRect;
    if (!q->icon().isNull())
    {
        iconRect = QRect(QPoint(rect.topLeft().x(), (q->height() - iconSize.height()) / 2), iconSize);
        p->drawPixmap(iconRect, UProxyStyle::getIconPixmap(state, q->icon(), iconSize));
    }

    int x = iconRect.isEmpty() ? rect.topLeft().x() : rect.topLeft().x() + iconRect.width() + 9;
    rect.setX(x);
    QFontMetrics fm(q->font());
    QString text = fm.elidedText(q->text(), Qt::TextElideMode::ElideMiddle, rect.width());
    p->drawText(rect, Qt::TextSingleLine | static_cast<int>(Qt::AlignLeft | Qt::AlignVCenter), text);
}

void UMenuButtonPrivate::drawBorder(const QStyle::State &state, QPainter *p)
{
    Q_Q(UMenuButton);
    QColor borderColor = q->palette().color(QPalette::Button);
    if (state & QStyle::State_MouseOver)
    {
        borderColor = q->palette().color(QPalette::ToolTipText);
    }

    if (q->isDown())
    {
        borderColor = q->palette().color(QPalette::Highlight);
    }

    if (borderColor.isValid())
    {
        p->setPen(QPen(borderColor, 1));
        p->drawRoundedRect(q->rect().adjusted(1, 1, -1, -1), 4, 4);
    }
}

UMenuButton::UMenuButton(QWidget* parent) : QPushButton(parent)
    , d_ptr(new UMenuButtonPrivate(this))
{
    Q_D(UMenuButton);
    setMenu(d->menu.data());
    
    connect(d->menu.data(), &QMenu::triggered, this, &UMenuButton::updateButtonContent);
}

UMenuButton::~UMenuButton()
{
}

QMenu* UMenuButton::buttonMenu() const
{
    Q_D(const UMenuButton);
    return d->menu.data();
}

QStandardItemModel* UMenuButton::model() const
{
    Q_D(const UMenuButton);
    return d->model;
}

void UMenuButton::addItems(QList<QStandardItem*> items)
{
    Q_D(UMenuButton);
    d->model->appendRow(items);

    for (const auto &item : items)
    {
        d->creatMenu(item, d->menu.data());
    }

    auto first = d->model->item(0, 0);
    if (first)
    {
        setIcon(first->icon());
        setText(first->text());
    }
}

void UMenuButton::addItem(QStandardItem* item)
{
    Q_D(UMenuButton);
    d->model->appendRow(item);
    d->creatMenu(item, d->menu.data());

    auto first = d->model->item(0, 0);
    if (first)
    {
        setIcon(first->icon());
        setText(first->text());
    }
}

void UMenuButton::clear()
{
    Q_D(UMenuButton);
    d->menu->clear();
    d->model->clear();
    d->actionMap.clear();
}

QStandardItem* UMenuButton::actionItem(QAction *action)
{
    Q_D(UMenuButton);
    if (nullptr == action)
    {
        return nullptr;
    }

    return d->actionMap[action];
}

void UMenuButton::updateButtonContent(QAction* action)
{
    if (nullptr == action)
    {
        return;
    }

    const QStandardItem* item = actionItem(action);
    if (nullptr == item)
    {
        return;
    }

    setText(item->text());
    setIcon(item->icon());
    emit triggered(action);
}

void UMenuButton::paintEvent(QPaintEvent*)
{
    Q_D(UMenuButton);
    QStylePainter p(this);
    QStyleOptionButton option;
    initStyleOption(&option);
    p.setRenderHints(QStylePainter::Antialiasing | QStylePainter::SmoothPixmapTransform);

    isEnabled() ? p.setOpacity(1) : p.setOpacity(0.4);
    QRect textRect = option.rect.adjusted(1, 1, -1, -1);
    textRect.setX(10);

    QPainterPath path;
    path.addRoundedRect(rect().adjusted(1, 1, -1, -1), 4, 4);
    p.fillPath(path, palette().color(QPalette::Button));

    if (option.features & QStyleOptionButton::HasMenu)
    {
        d->drawArrow(option.state, &p);
    }

    d->drawContent(textRect, option.state, &p);
    d->drawBorder(option.state, &p);
}

QSize UMenuButton::sizeHint() const
{
    return QSize(QPushButton::sizeHint().width(), 30);
}

void UMenuButton::resizeEvent(QResizeEvent *event)
{
    Q_D(UMenuButton);
    d->menu->setFixedWidth(width());
    QPushButton::resizeEvent(event);
}
